package com.dc.dbus.session.server;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;

import com.dc.dbus.session.RC;
import com.dc.dbus.session.annotation.ReqType;
import com.dc.dbus.session.bean.FutureResult;
import com.dc.dbus.session.exception.RemoteException;
import com.dc.dbus.session.listener.NoExceptionServerSideEventListener;
import com.dc.dbus.session.listener.ServerSideEventListener;
import com.dc.dbus.transport.commons.ClientSideDisconnectException;
import com.dc.dbus.transport.commons.ServerSideReceiver;
import com.dc.dbus.transport.commons.ServerSideTransport;

public class DefaultServerStub implements ServerStub, ServerSideReceiver {

	protected final ServerSideTransport serverSideTansport;
	
	protected final HashMap<Integer, CmdInfo> cmdMapInfo = new HashMap<Integer, CmdInfo>();
	
	protected final ExecutorService executor;
	
	protected final NoExceptionServerSideEventListener eventListener;
	
	public DefaultServerStub(ServerSideTransport serverSideTansport, ExecutorService executor, 
			ServerSideEventListener eventListener) {
		this.serverSideTansport = serverSideTansport;
		this.executor = executor;
		this.eventListener = new NoExceptionServerSideEventListener(eventListener);
	}
	
	@Override
	public void close() {
		
		serverSideTansport.close();
		
		if (executor != null) {
			executor.shutdown();
		}
		
	}

	@Override
	public void registerInvoker(int cmd, CmdInfo cmdInfo) {
		
		if (cmdMapInfo.containsKey(cmd)) {
			CmdInfo oldCmdInfo = cmdMapInfo.get(cmd);
			throw new RuntimeException(
					"interface(" + cmdInfo.instance.getClass().getName() + ") method(" + cmdInfo.method.getName() 
					+ ")'s cmd(" + cmd + ") is same with interface(" 
					+ oldCmdInfo.instance.getClass().getName() + ") method(" + oldCmdInfo.method.getName() + ")" );
		} else {
			cmdMapInfo.put(cmd, cmdInfo);
		}
	}
	
	@SuppressWarnings("rawtypes")
	protected void handle(String source, long msgId, int cmd, Object param) {
		
		CmdInfo cmdInfo = cmdMapInfo.get(cmd);
		try {
			
			if (cmdInfo != null) {
				Object returnObj = cmdInfo.method.invoke(cmdInfo.instance, (Object[]) param);
				
				switch (cmdInfo.reqType) {
				case SYNC:
					serverSideTansport.sendBack(source, msgId, cmd, RC.SUCCESS, returnObj);
					break;
				case ASYNC:
					serverSideTansport.sendBack(source, msgId, cmd, RC.SUCCESS, ((FutureResult) returnObj).get());
					break;
				default:
					break;
				}
			} else {
				
				eventListener.dbusException("DefaultServerStub.handle unknow cmd:" + cmd 
						+ " for (from:" + source + ", msgId:" + msgId + ", cmd:" + cmd + ", param:" + paramToString(param) + ")", 
						new RuntimeException());
			}
			
		} catch (InvocationTargetException e) {
            
			if (cmdInfo.reqType != ReqType.MSG && source != null) { // 消息类型不需要给服务端返回异常信息
				
				if (e.getTargetException() instanceof RemoteException) {
	                
					serverSideTansport.sendBack(source, msgId, cmd, 
                			((RemoteException) e.getTargetException()).getErrorCode(), null);
	                
	            } else {
	                
	            	if (e.getTargetException() instanceof Serializable) {
	            		
	            		serverSideTansport.sendBack(source, msgId, cmd, 
	                    		RC.UNKNOW, e.getTargetException());
	            		
	            	} else {
	            		
	            		serverSideTansport.sendBack(source, msgId, cmd, 
	                    		RC.UNKNOW, null);
	            	}
	            }
			}
			
            eventListener.methodExcuteException("", e.getTargetException());
            
        } catch (ClientSideDisconnectException e) {
        	
        	eventListener.dbusException("", e);
        	
        } catch (Exception e) {
        	
        	eventListener.dbusException("", e);
        	
            if (cmdInfo.reqType != ReqType.MSG && source != null) { // 消息类型不需要给服务端返回异常信息
            	if (e instanceof Serializable) {
            		serverSideTansport.sendBack(source, msgId, cmd, RC.UNKNOW, e);
            	} else {
            		serverSideTansport.sendBack(source, msgId, cmd, RC.UNKNOW, null);
            	}
            }
		} 
	}
	
	protected String paramToString(Object param) {
		
		if (param instanceof Object[]) {
			return Arrays.toString((Object[])param);
		} else {
			return param.toString();
		}
	}
	
	@Override
	public void handlerMsg(final String source, final long msgId, final int cmd, final Object param) {
		
		if (executor != null) {
			try {
				executor.execute(new Runnable() {
					@Override
					public void run() {
						handle(source, msgId, cmd, param);
					}
				});
			} catch (RejectedExecutionException e) {
				if (source != null) {
					serverSideTansport.sendBack(source, msgId, cmd, RC.QUEUE_FULL, null);
				}
				
				eventListener.queueFull("DefaultServerStub.handleMessage QUEUE_FULL, for (from:" + source 
						+ ", msgId:" + msgId + ", cmd:" + cmd + ", param:" + paramToString(param) + ")", 
						e);
			}
		} else {
			this.handle(source, msgId, cmd, param);
		}
	}



	
	
}
